/*
 * Copyright (C) 2015 Daniel Uppenkamp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.maurersmarbles.states;

import com.google.common.base.Preconditions;
import com.googlecode.maurersmarbles.Board;
import com.googlecode.maurersmarbles.Card;
import com.googlecode.maurersmarbles.CardsDisplay;
import com.googlecode.maurersmarbles.GameLogic;
import com.googlecode.maurersmarbles.GameState;
import com.googlecode.maurersmarbles.Marble;
import com.googlecode.maurersmarbles.Orientation;
import com.googlecode.maurersmarbles.Player;
import com.googlecode.maurersmarbles.RGB;
import com.googlecode.maurersmarbles.Rectangle;
import com.googlecode.maurersmarbles.Screen;
import com.googlecode.maurersmarbles.StatusDisplay;
import com.googlecode.maurersmarbles.drawables.BoardDrawable;
import com.googlecode.maurersmarbles.drawables.ButtonDrawable;
import com.googlecode.maurersmarbles.drawables.CardDrawable;
import com.googlecode.maurersmarbles.drawables.DealerChipDrawable;
import com.googlecode.maurersmarbles.drawables.Drawable;
import com.googlecode.maurersmarbles.drawables.MarbleDrawable;
import com.googlecode.maurersmarbles.drawables.RectangleDrawable;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 *
 * @author Daniel Uppenkamp
 */
public abstract class TableState implements State {

    /**
     * The ratio of the player card that is one the board for the cards display.
     */
    protected static final float PLAYER_CARD_ON_BOARD_RATIO = 0.5f;
    /**
     * The ratio of the card size to the board size.
     */
    protected static final float SCREEN_SIZE_TO_BOARD_SIZE = 0.95f;
    /**
     * The board drawable that is used to draw the board on the table.
     */
    protected static final BoardDrawable boardDrawable = new BoardDrawable();
    /**
     * The discarded card.
     */
    protected static Card discardedCard = null;
    /**
     * The map that contains the cards display for each player.
     */
    protected static final Map<Integer, CardsDisplay> cardsDisplayMap = new HashMap();
    /**
     * The status display.
     */
    protected static StatusDisplay statusDisplay = new StatusDisplay();

    static {

        // create the cards display for player 0, with face up cards
        CardsDisplay cardsDisplay = new CardsDisplay(true);
        cardsDisplay.setOrientation(Orientation.NORTH);
        cardsDisplayMap.put(0, cardsDisplay);

        // create the cards display for player 1, with face down cards
        cardsDisplay = new CardsDisplay(false);
        cardsDisplay.setOrientation(Orientation.EAST);
        cardsDisplayMap.put(1, cardsDisplay);

        // create the cards display for player 2, with face down cards
        cardsDisplay = new CardsDisplay(false);
        cardsDisplay.setOrientation(Orientation.SOUTH);
        cardsDisplayMap.put(2, cardsDisplay);

        // create the cards display for player 3, with face down cards
        cardsDisplay = new CardsDisplay(false);
        cardsDisplay.setOrientation(Orientation.WEST);
        cardsDisplayMap.put(3, cardsDisplay);

    }

    /**
     * Returns the drawables for the table.
     *
     * @param screen the screen
     * @param gameState the game state
     * @return the drawable for the table
     */
    protected Map<Object, Drawable> getDrawables(Screen screen, GameState gameState) {

        Preconditions.checkNotNull(screen, "Null screen");
        Preconditions.checkNotNull(gameState, "Null game state");
        Preconditions.checkNotNull(boardDrawable, "Null board drawable");
        Preconditions.checkNotNull(cardsDisplayMap, "Null cards display map");
        Preconditions.checkNotNull(statusDisplay, "Null status display");

        LinkedHashMap<Object, Drawable> drawables = new LinkedHashMap();

        RectangleDrawable rectangleDrawable = new RectangleDrawable(0, 0, screen.getWidth(), screen.getHeight(), RGB.WHITE, true);
        drawables.put(rectangleDrawable, rectangleDrawable);

        // TODO Add drawables for the table
        // compute the board size and location
        int boardSize = (int) (screen.getWidth() * SCREEN_SIZE_TO_BOARD_SIZE);
        int boardStartXY = (int) ((screen.getWidth() - boardSize) / 2.0);
        boardDrawable.setRectangle(new Rectangle(boardStartXY, boardStartXY, boardSize, boardSize));
        boardDrawable.setUserMarbleColorIndex(gameState.getUserMarbleColorIndex());

        // add the drawable for the board
        drawables.put(boardDrawable, boardDrawable);

        // add the drawables for the marbles
        for (Player player : gameState.getPlayers()) {
            for (Marble marble : player.getMarbles()) {
                int rgb = GameLogic.getMarbleColorForPlayer(gameState, player.getPlayerNumber());
                Rectangle rectangle = boardDrawable.getRectangleForMarbleSpace(marble.getBoardIndex());
                drawables.put(marble, new MarbleDrawable(rgb, rectangle));
            }
        }

        // add the drawables for the player cards
        for (int i = 0; i < GameLogic.NUMBER_OF_PLAYERS; i++) {

            // get the rectangle to draw the cards display in
            Rectangle rectangle = getRectangleForCardsDisplay(i);

            // update the cards display
            CardsDisplay cardsDisplay = cardsDisplayMap.get(i);
            cardsDisplay.setRectangle(rectangle);

            /**
             * For player 1, 2, and 3, set the card sizes to match the discarded
             * card on the board.
             */
            if (i != 0) {
                Rectangle discardRectangle = boardDrawable.getRectangleForDiscardCard();
                cardsDisplay.setCardSize(discardRectangle.getWidth(), discardRectangle.getHeight());
            }

            drawables.putAll(cardsDisplay.getDrawables(gameState.getPlayers().get(i).getCards()));

        }

        // add the drawable for the dealer chip
        DealerChipDrawable dealerChipDrawable = new DealerChipDrawable(boardDrawable.getRectangleForDealerChip(gameState.getDealer()));
        drawables.put(dealerChipDrawable, dealerChipDrawable);

        // add the drawable for the discarded card, if it is not null
        if (discardedCard != null) {
            drawables.put(discardedCard, new CardDrawable(discardedCard, boardDrawable.getRectangleForDiscardCard()));
        }

        // add the drawable for the card deck, if it is not empty
        if (!gameState.getCardDeck().isEmpty()) {
            CardDrawable cardDrawable = new CardDrawable(null, boardDrawable.getRectangleForCardDeck());
            drawables.put(cardDrawable, cardDrawable);
        }
        
        // add the drawables for the status display
        statusDisplay.setRectangle(getRectangleForStatusDisplay(screen));
        drawables.putAll(statusDisplay.getDrawables(gameState));
        
//        int spacing = getSpacing();
//        double buttonHeight = screen.getHeight() * ButtonDrawable.SCREEN_HEIGHT_TO_BUTTON_HEIGHT;
//        double buttonWidth = screen.getWidth() / 5;
        
//        ButtonDrawable buttonDrawable = new ButtonDrawable("Back");
//        buttonDrawable.setRectangle(new Rectangle((int)(spacing), (int)(screen.getHeight() - spacing - buttonHeight), (int)buttonWidth, (int)buttonHeight));
//        drawables.put(buttonDrawable, buttonDrawable);
//        
//        buttonDrawable = new ButtonDrawable("Help");
//        buttonDrawable.setRectangle(new Rectangle((int)(screen.getWidth() - spacing - buttonWidth), (int)(screen.getHeight() - spacing - buttonHeight), (int)buttonWidth, (int)buttonHeight));
//        drawables.put(buttonDrawable, buttonDrawable);
//        
//        buttonDrawable = new ButtonDrawable("Options");
//        buttonDrawable.setRectangle(new Rectangle((int)(screen.getWidth() - spacing - buttonWidth - spacing - buttonWidth), (int)(screen.getHeight() - spacing - buttonHeight), (int)buttonWidth, (int)buttonHeight));
//        drawables.put(buttonDrawable, buttonDrawable);

        return drawables;

    }

    /**
     * Returns the rectangle for the given player number's cards display.
     *
     * @param playerNumber the player number
     * @return the rectangle for the given player number's cards display
     */
    protected Rectangle getRectangleForCardsDisplay(int playerNumber) {

        Preconditions.checkNotNull(boardDrawable, "Null board drawable");

        Rectangle boardRectangle = boardDrawable.getRectangle();

        int startX = 0;
        int startY = 0;
        Rectangle homeEntryRectangle = boardDrawable.getRectangleForBoardSpace(Board.getGridCellForBoardIndex(Board.BOARD_INDEX_HOME_ENTRY[playerNumber]));

        switch (playerNumber) {
            case 0:
                startX = boardRectangle.getLeft();
                startY = boardRectangle.getBottom() + getSpacing();//(int) (boardRectangle.getWidth() * (1 - SCREEN_SIZE_TO_BOARD_SIZE) / 2.0);
                break;
            case 1:
                startX = boardRectangle.getLeft() - boardRectangle.getWidth() + (int) ((homeEntryRectangle.getLeft() - boardRectangle.getLeft()) * PLAYER_CARD_ON_BOARD_RATIO);
                startY = boardRectangle.getTop();
                break;
            case 2:
                startX = boardRectangle.getLeft();
                startY = boardRectangle.getTop() - boardRectangle.getHeight() + (int) ((homeEntryRectangle.getTop() - boardRectangle.getTop()) * PLAYER_CARD_ON_BOARD_RATIO);
                break;
            case 3:
                startX = boardRectangle.getRight() - (int) ((boardRectangle.getRight() - homeEntryRectangle.getRight()) * PLAYER_CARD_ON_BOARD_RATIO);
                startY = boardRectangle.getTop();
                break;
        }

        return new Rectangle(startX, startY, boardRectangle.getWidth(), boardRectangle.getHeight());

    }

    /**
     * Returns the rectangle for the status display.
     *
     * @param screen the screen
     * @return the rectangle for the status display
     */
    protected Rectangle getRectangleForStatusDisplay(Screen screen) {

        Preconditions.checkNotNull(screen, "Null screen");
        Preconditions.checkNotNull(cardsDisplayMap, "Null cards display map");

        Rectangle cardsDisplayRectangle = getRectangleForCardsDisplay(0);

        int spacing = getSpacing();
        int startX = cardsDisplayRectangle.getX();
        int startY = (int) (cardsDisplayRectangle.getY() + cardsDisplayMap.get(0).getCardHeight() + spacing);
        int width = (int) cardsDisplayRectangle.getWidth();
        int height = (int)(screen.getHeight() - spacing - (screen.getHeight() * ButtonDrawable.SCREEN_HEIGHT_TO_BUTTON_HEIGHT) - spacing - startY - 1);

        return new Rectangle(startX, startY, width, height);

    }

    /**
     * Returns the rectangle for the given player numbers card.
     *
     * @param screen the screen
     * @param gameState the game state
     * @param playerNumber the player number
     * @param card the card, only used for player 0
     * @return the rectangle for the given player numbers card
     */
    protected Rectangle getRectangleForPlayerCard(Screen screen, GameState gameState, int playerNumber, Card card) {

        Preconditions.checkNotNull(screen, "Null screen");
        Preconditions.checkNotNull(gameState, "Null game state");
        Preconditions.checkNotNull(boardDrawable, "Null board drawable");
        Preconditions.checkNotNull(cardsDisplayMap, "Null cards display map");

        // get the cards display for the player on the bottom of the board
        CardsDisplay cardsDisplay = cardsDisplayMap.get(0);

        double cardWidth = cardsDisplay.getCardWidth();
        double cardHeight = cardsDisplay.getCardHeight();

        Rectangle homeEntryRectangle = boardDrawable.getRectangleForMarbleSpace(Board.BOARD_INDEX_HOME_ENTRY[playerNumber]);

        int startX = 0;
        int startY = 0;
        switch (playerNumber) {
            case 0:
                return cardsDisplay.getRectangleForCard(card);
            case 1:
                startX = (int) -cardWidth;
                startY = (int) (homeEntryRectangle.getCenterY() - (cardHeight / 2.0));
                break;
            case 2:
                startX = (int) (homeEntryRectangle.getCenterX() - (cardWidth / 2.0));
                startY = (int) -cardHeight;
                break;
            case 3:
                startX = screen.getWidth() - 1;
                startY = (int) (homeEntryRectangle.getCenterY() - (cardHeight / 2.0));
                break;
        }

        return new Rectangle(startX, startY, (int) cardWidth, (int) cardHeight);

    }

    /**
     * Returns the spacing between the components on the table.
     * @return the spacing between the components on the table
     */
    public int getSpacing() {        
        Preconditions.checkNotNull(boardDrawable, "Null board drawable");        
        return boardDrawable.getRectangle().getLeft();
    }
    
}
